﻿using PagedList;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Tippelde.Entities;
using Tippelde.Services.Dtos;
using Tippelde.Services.Exceptions;

namespace Tippelde.Services.News
{
    public class NewsManager : INewsManager
    {

        private readonly Expression<Func<Tippelde.Entities.News, NewsHeaderData>> NewsHeaderSelector =
            n => new NewsHeaderData
            {
                Id = n.Id,
                LastEdited = n.LastEdited,
                AddedAt = n.AddedAt,
                Title = n.Title,
                Content = n.Content,
                ContentShort = n.ContentShort,
                SeoUrl = n.SeoUrl,
                AllowAnonym = n.AllowAnonym,
                AllowGlobalAuthenticated = n.AllowGlobalAuthenticated,
                Creator = new UserHeaderData
                {
                    FullName = n.Creator.FullName,
                    UserName = n.Creator.UserName,
                    Email = n.Creator.Email,
                    UserId = n.Creator.Id
                }
            };

        private readonly Expression<Func<Tippelde.Entities.News, NewsWithRightsData>> NewsWithRightsSelector =
            n => new NewsWithRightsData
            {
                Id = n.Id,
                LastEdited = n.LastEdited,
                AddedAt = n.AddedAt,
                Title = n.Title,
                Content = n.Content,
                ContentShort = n.ContentShort,
                SeoUrl = n.SeoUrl,
                AllowAnonym = n.AllowAnonym,
                AllowGlobalAuthenticated = n.AllowGlobalAuthenticated,
                Creator = new UserHeaderData
                {
                    FullName = n.Creator.FullName,
                    UserName = n.Creator.UserName,
                    Email = n.Creator.Email,
                    UserId = n.Creator.Id
                }
            };

        public List<NewsHeaderData> GetTopNews(string userName, int? count)
        {
            using (var ctx = new TippeldeEntities())
            {
                if (String.IsNullOrEmpty(userName))
                {
                    return ctx.News.Where(n => n.AllowAnonym).OrderByDescending(n => n.AddedAt).Select(NewsHeaderSelector).Take(count ?? 5).ToList();
                }
                else
                {
                    var usersGroupIds = ctx.Users.Include(u => u.UserGroups).Single(u => u.UserName == userName).UserGroups.Select(ug => ug.GroupId);
                    return ctx.News.Where(n => n.AllowAnonym || n.AllowGlobalAuthenticated || n.Groups.Any(g => usersGroupIds.Contains(g.Id))).OrderByDescending(n => n.AddedAt).Select(NewsHeaderSelector).Take(count ?? 5).ToList();
                }
            }
        }

        public IPagedList<NewsWithRightsData> GetNews(string userName, int? page)
        {
            using (var ctx = new TippeldeEntities())
            {
                if (String.IsNullOrEmpty(userName))
                {
                    return ctx.News.Where(n => n.AllowAnonym).OrderByDescending(n => n.AddedAt).Select(NewsWithRightsSelector).ToPagedList(page ?? 1, 10);
                }
                else
                {
                    var usersGroupIds = ctx.Users.Include(u => u.UserGroups).Single(u => u.UserName == userName).UserGroups.Select(ug => ug.GroupId);
                    var news = ctx.News.Where(n => n.AllowAnonym || n.AllowGlobalAuthenticated || n.Groups.Any(g => usersGroupIds.Contains(g.Id))).OrderByDescending(n => n.AddedAt).Select(NewsWithRightsSelector).ToPagedList(page ?? 1, 10);
                    foreach (var item in news)
                    {
                        try
                        {
                            item.IsUserEditor = ctx.UserGroups.Any(ug => ug.User.UserName == userName && ug.IsEditor && ug.Group.News.Any(ee => ee.Id == item.Id));
                        }
                        catch (Exception) { }
                    }
                    return news;
                }
            }
        }

        public PagedList.IPagedList<NewsWithRightsData> GetNewsForGroup(string userName, int groupId, int? page)
        {
            using (var ctx = new TippeldeEntities())
            {
                var userGroup = ctx.UserGroups.SingleOrDefault(ug => ug.User.UserName == userName && ug.GroupId == groupId);
                if (userGroup == null)
                    throw new TippeldeException("A felhasználó nem tagja a csoportnak!");
                var news = ctx.News.Where(n => n.Groups.Any(g => g.Id == groupId)).OrderByDescending(n => n.AddedAt).Select(NewsWithRightsSelector).ToPagedList(page ?? 1, 10);
                foreach (var item in news)
                {
                    try
                    {
                        item.IsUserEditor = ctx.UserGroups.Any(ug => ug.User.UserName == userName && ug.IsEditor && ug.Group.News.Any(ee => ee.Id == item.Id));
                    }
                    catch (Exception) { }
                }
                return news;
            }
        }

        public NewsWithRightsData GetNews(string userName, int newsId)
        {
            using (var ctx = new TippeldeEntities())
            {
                if (userName == null)
                {
                    try
                    {
                        return ctx.News.Where(n => n.Id == newsId && n.AllowAnonym).Select(NewsWithRightsSelector).Single();
                    }
                    catch (Exception)
                    {
                        throw new TippeldeException("Nincs ilyen hír, vagy nem jogosult az olvasására!");
                    }
                }
                else
                {
                    var news = ctx.News.SingleOrDefault(n => n.Id == newsId);
                    var usersGroupIds = ctx.Users.Include(u => u.UserGroups).Single(u => u.UserName == userName).UserGroups.Select(ug => ug.GroupId);
                    if (news == null || !news.AllowAnonym && !news.AllowGlobalAuthenticated && !news.Groups.Any(g => usersGroupIds.Contains(g.Id)))
                    {
                        throw new TippeldeException("Nincs ilyen hír, vagy nem jogosult az olvasására!");
                    }
                    var newsSelector = NewsWithRightsSelector.Compile();
                    var newsHeader = newsSelector(news);
                    try
                    {
                        newsHeader.IsUserEditor = ctx.UserGroups.Any(ug => ug.User.UserName == userName && ug.IsEditor && ug.Group.News.Any(ee => ee.Id == newsHeader.Id));
                    }
                    catch (Exception) { }
                    return newsHeader;
                }
            }
        }

        public NewsWithRightsData GetNews(string userName, string seoUrl)
        {
            using (var ctx = new TippeldeEntities())
            {
                if (userName == null)
                {
                    try
                    {
                        return ctx.News.Where(n => n.SeoUrl == seoUrl && n.AllowAnonym).Select(NewsWithRightsSelector).Single();
                    }
                    catch (Exception)
                    {
                        throw new TippeldeException("Nincs ilyen hír, vagy nem jogosult az olvasására!");
                    }
                }
                else
                {
                    var news = ctx.News.SingleOrDefault(n => n.SeoUrl == seoUrl);
                    var usersGroupIds = ctx.Users.Include(u => u.UserGroups).Single(u => u.UserName == userName).UserGroups.Select(ug => ug.GroupId);
                    if (news == null || !news.AllowAnonym && !news.AllowGlobalAuthenticated && !news.Groups.Any(g => usersGroupIds.Contains(g.Id)))
                    {
                        throw new TippeldeException("Nincs ilyen hír, vagy nem jogosult az olvasására!");
                    }
                    var newsSelector = NewsWithRightsSelector.Compile();

                    var newsHeader = newsSelector(news);
                    try
                    {
                        newsHeader.IsUserEditor = ctx.UserGroups.Any(ug => ug.User.UserName == userName && ug.IsEditor && ug.Group.News.Any(ee => ee.Id == newsHeader.Id));
                    }
                    catch (Exception) { }
                    return newsHeader;
                }
            }
        }

        public void Create(string creatorName, NewsHeaderData newsHeader, List<int> groupsIds, string contentShortParsed)
        {
            using (var ctx = new TippeldeEntities())
            {
                var creator = ctx.Users.Single(u => u.UserName == creatorName);
                var userGroups = creator.UserGroups.Where(ug => ug.IsEditor).Select(ug => ug.Group).ToList();
                var contentShort = contentShortParsed ?? newsHeader.Content;
                contentShort = contentShort.Length >= 120 ? contentShort.Substring(0, 120) : contentShort;
                if (newsHeader.Content.Length >= 120)
                    contentShort = String.Format("{0}...", contentShort.Substring(0, contentShort.LastIndexOf(" ")));

                var news = new Tippelde.Entities.News()
                {
                    Creator = creator,
                    Groups = userGroups.Where(g => groupsIds.Contains(g.Id)).ToList(),
                    AddedAt = DateTime.Now,
                    Title = newsHeader.Title,
                    Content = newsHeader.Content,
                    ContentShort = contentShort,
                    SeoUrl = newsHeader.SeoUrl,
                    AllowAnonym = newsHeader.AllowAnonym,
                    AllowGlobalAuthenticated = newsHeader.AllowGlobalAuthenticated
                };
                ctx.News.Add(news);
                ctx.SaveChanges();
            }
        }

        public void Edit(string editorName, NewsHeaderData newsHeader, List<int> groupsIds, string contentShortParsed)
        {
            using (var ctx = new TippeldeEntities())
            {
                var editor = ctx.Users.Single(u => u.UserName == editorName);
                var userGroups = editor.UserGroups.Where(ug => ug.IsEditor).Select(ug => ug.Group).ToList();

                var news = ctx.News.Single(n => n.Id == newsHeader.Id);
                var contentShort = contentShortParsed ?? newsHeader.Content;
                contentShort = contentShort.Length >= 120 ? contentShort.Substring(0, 120) : contentShort;
                if (newsHeader.Content.Length >= 120)
                    contentShort = String.Format("{0}...", contentShort.Substring(0, contentShort.LastIndexOf(" ")));

                news.Groups = userGroups.Where(g => groupsIds.Contains(g.Id)).ToList();
                news.LastEdited = DateTime.Now;
                news.Title = newsHeader.Title;
                news.Content = newsHeader.Content;
                news.ContentShort = contentShort;
                news.AllowAnonym = newsHeader.AllowAnonym;
                news.AllowGlobalAuthenticated = newsHeader.AllowGlobalAuthenticated;

                ctx.SaveChanges();
            }
        }


        public void Delete(int newsId)
        {
            using (var ctx = new TippeldeEntities())
            {
                var news = new Tippelde.Entities.News { Id = newsId };
                ctx.Entry(news).State = EntityState.Deleted;
                ctx.SaveChanges();
            }
        }
    }
}
